//! Implementation block and method parsers

use super::super::{parse_type, Parser};
use super::helpers::parse_where_clause_for_generics;
use crate::ast::decl::*;
use crate::ast::types::*;
use crate::ast::{Block, FunctionDecl};
use crate::error::{CompilerError, Result};
use crate::lexer::token::Token;
use crate::parser::stmt_parser::parse_statement;

/// Parse an implementation declaration
/// This handles: impl Type { ... } or impl Option<T> { ... }
/// For trait implementations, use parse_trait_impl_declaration
pub fn parse_impl_declaration(parser: &mut Parser) -> Result<ImplDecl> {
    parser.expect(&Token::Impl)?;

    // Parse type name (may include generic parameters like Option<T>)
    let type_name = if let Some(Token::Identifier(name)) = parser.peek().map(|t| &t.token) {
        let name = name.clone();
        parser.advance()?;

        // If we see '<' after the identifier, it's type generics (e.g., Option<T>)
        // Skip them for now - we only need the base type name for method naming
        if parser.check(&Token::Less) {
            parser.advance()?; // consume '<'
                               // Skip generic parameters (e.g., <T>, <T, U>, etc.)
            let mut depth = 1;
            while depth > 0 && !parser.is_at_end() {
                if parser.check(&Token::Less) {
                    depth += 1;
                    parser.advance()?;
                } else if parser.check(&Token::Greater) {
                    depth -= 1;
                    if depth == 0 {
                        parser.advance()?;
                        break;
                    } else {
                        parser.advance()?;
                    }
                } else {
                    parser.advance()?;
                }
            }
        }

        name
    } else {
        return Err(CompilerError::syntax(
            parser.current_location().line,
            parser.current_location().column,
            "Expected type name after 'impl'",
        ));
    };

    // Check if this is "impl Trait for Type" syntax - if so, this is an error
    // because we should use parse_trait_impl_declaration for that
    if parser.check(&Token::For) {
        return Err(CompilerError::syntax(
            parser.current_location().line,
            parser.current_location().column,
            "Found 'for' keyword. Use parse_trait_impl_declaration for trait implementations.",
        ));
    }

    // Parse optional impl generic parameters (impl<T> Type { ... })
    let mut generics = if parser.consume(&Token::Less)? {
        let mut params = Vec::new();
        loop {
            if let Some(Token::Identifier(name)) = parser.peek().map(|t| &t.token) {
                let param_name = name.clone();
                parser.advance()?;

                let constraints = if parser.consume(&Token::Colon)? {
                    let mut constraint_list = Vec::new();
                    loop {
                        if let Some(Token::Identifier(_constraint)) =
                            parser.peek().map(|t| &t.token)
                        {
                            constraint_list.push(Type::Basic(crate::ast::types::BasicType::String));
                            parser.advance()?;

                            if !parser.consume(&Token::Plus)? {
                                break;
                            }
                        } else {
                            break;
                        }
                    }
                    constraint_list
                } else {
                    Vec::new()
                };

                params.push(GenericParam {
                    name: param_name,
                    constraints,
                    location: parser.current_location(),
                });

                if !parser.consume(&Token::Comma)? {
                    break;
                }
            } else {
                break;
            }
        }

        parser.expect(&Token::Greater)?;
        params
    } else {
        Vec::new()
    };

    // Optional where-clause before '{'
    parse_where_clause_for_generics(parser, &mut generics)?;

    // Parse opening brace
    parser.expect(&Token::LeftBrace)?;

    // Parse methods
    let mut methods = Vec::new();
    while !parser.consume(&Token::RightBrace)? {
        if parser.is_at_end() {
            return Err(CompilerError::syntax(
                parser.current_location().line,
                parser.current_location().column,
                "Expected '}' to close impl block",
            ));
        }

        // Skip comments and other non-method tokens
        if parser.check(&Token::Fn) {
            let method = parse_impl_method(parser, &type_name)?;
            methods.push(method);
        } else {
            // Skip unknown tokens (like comments)
            parser.advance()?;
        }
    }

    Ok(ImplDecl {
        type_name,
        generics,
        methods,
        location: parser.current_location(),
    })
}

/// Parse a method within an impl block
pub(in crate::parser) fn parse_impl_method(
    parser: &mut Parser,
    type_name: &str,
) -> Result<FunctionDecl> {
    parser.expect(&Token::Fn)?;

    // Parse method name (allow keywords like 'new', 'init', 'drop' as method names)
    let method_name = match parser.peek().map(|t| &t.token) {
        Some(Token::Identifier(name)) => {
            let name = name.clone();
            parser.advance()?;
            name
        }
        Some(Token::New) => {
            parser.advance()?;
            "new".to_string()
        }
        Some(Token::Init) => {
            parser.advance()?;
            "init".to_string()
        }
        Some(Token::Drop) => {
            parser.advance()?;
            "drop".to_string()
        }
        _ => {
            return Err(CompilerError::syntax(
                parser.current_location().line,
                parser.current_location().column,
                "Expected method name after 'fn'",
            ));
        }
    };

    // Parse parameters
    parser.expect(&Token::LeftParen)?;
    let mut parameters = Vec::new();

    // Track if self parameter was explicitly provided
    let has_self = false;

    // Parse parameters (including self)
    if !parser.consume(&Token::RightParen)? {
        loop {
            // Check for &self parameter first
            // In impl blocks, self is implicit (like C++ this pointer), so explicit &self should be an error
            if parser.check(&Token::Ampersand) {
                parser.advance()?; // consume '&'
                if let Some(Token::Identifier(param_name)) = parser.peek().map(|t| &t.token) {
                    if param_name == "self" {
                        return Err(CompilerError::syntax(
                            parser.current_location().line,
                            parser.current_location().column,
                            "Explicit '&self' parameter is not allowed in impl blocks. 'self' is implicit (like C++ 'this'). Remove '&self' from the parameter list.",
                        ));
                    }
                }
                return Err(CompilerError::syntax(
                    parser.current_location().line,
                    parser.current_location().column,
                    "Expected 'self' after '&' in parameter",
                ));
            }

            // Parse parameter name (including self)
            if let Some(Token::Identifier(param_name)) = parser.peek().map(|t| &t.token) {
                let name = param_name.clone();
                parser.advance()?;

                // Check if this is a self parameter (with or without explicit type)
                // In impl blocks, self is implicit, so explicit self should be an error
                if name == "self" {
                    return Err(CompilerError::syntax(
                        parser.current_location().line,
                        parser.current_location().column,
                        "Explicit 'self' parameter is not allowed in impl blocks. 'self' is implicit (like C++ 'this'). Remove 'self' from the parameter list.",
                    ));
                } else {
                    // Parse parameter type (required for non-self parameters)
                    // For NRC syntax, type comes directly after parameter name (no colon)
                    let param_type = if let Some(ty) = parse_type(parser)? {
                        ty
                    } else {
                        return Err(CompilerError::syntax(
                            parser.current_location().line,
                            parser.current_location().column,
                            "Expected parameter type after parameter name",
                        ));
                    };

                    parameters.push(crate::ast::types::Parameter {
                        name,
                        param_type,
                        default_value: None,
                        location: parser.current_location(),
                    });
                }
            } else {
                // If we don't find an identifier, we might be at the end of parameters
                // or there's a syntax error
                break;
            }

            // Check for comma (more parameters) or closing paren (end of parameters)
            if !parser.consume(&Token::Comma)? {
                parser.expect(&Token::RightParen)?;
                break;
            }
        }
    }

    // If no self parameter was found, add implicit &mut self at the beginning
    // This implements C++-style implicit 'this' pointer
    // By default, we add &mut self (mutable reference) so methods can modify fields
    // The type checker can later optimize to &self if the function doesn't mutate self
    if !has_self {
        let struct_type = Type::Struct(StructType {
            name: type_name.to_string(),
            type_args: vec![],
            location: parser.current_location(),
        });

        // Use &mut self by default (mutable reference)
        // In NRC, &T is already mutable, so Reference type is mutable by default
        let param_type = Type::Reference(crate::ast::types::ReferenceType {
            referenced_type: Box::new(struct_type),
            location: parser.current_location(),
        });

        // Insert self at the beginning of parameters list (C++-style)
        parameters.insert(
            0,
            crate::ast::types::Parameter {
                name: "self".to_string(),
                param_type,
                default_value: None,
                location: parser.current_location(),
            },
        );
    }

    // Parse return type
    // In NRC, return type can be specified with -> or : or directly after ()
    let return_type = if parser.consume(&Token::Arrow)? || parser.consume(&Token::Colon)? {
        parse_type(parser)?
    } else if !parser.check(&Token::LeftBrace) {
        // Try to parse type directly (for "func name() Type {" syntax)
        // But only if next token is not {
        parse_type(parser)?
    } else {
        None
    };

    // Parse body
    let body = if parser.consume(&Token::LeftBrace)? {
        let mut statements = Vec::new();
        while !parser.consume(&Token::RightBrace)? {
            if parser.is_at_end() {
                return Err(CompilerError::syntax(
                    parser.current_location().line,
                    parser.current_location().column,
                    "Expected '}' to close function body",
                ));
            }

            if let Some(stmt) = parse_statement(parser)? {
                statements.push(stmt);
            }
        }
        Some(Block {
            statements,
            location: parser.current_location(),
        })
    } else {
        None
    };

    // Add special attributes for init and drop methods
    let mut attributes = Vec::new();
    if method_name == "init" {
        attributes.push(Attribute {
            name: "constructor".to_string(),
            arguments: vec![],
            location: parser.current_location(),
        });
    } else if method_name == "drop" {
        attributes.push(Attribute {
            name: "destructor".to_string(),
            arguments: vec![],
            location: parser.current_location(),
        });
    }

    Ok(FunctionDecl {
        name: method_name,
        generics: vec![],
        parameters,
        return_type,
        body,
        attributes,
        location: parser.current_location(),
    })
}

/// Parse method declaration (Go-style: func (receiver Type) methodName() returnType)
pub(in crate::parser) fn parse_method_declaration(
    parser: &mut Parser,
) -> Result<Option<FunctionDecl>> {
    // Parse receiver (Go-style: (receiver Type))
    parser.expect(&Token::LeftParen)?;

    let _receiver_name = if let Some(Token::Identifier(name)) = parser.peek().map(|t| &t.token) {
        let name = name.clone();
        parser.advance()?;
        name
    } else {
        return Err(CompilerError::syntax(
            parser.current_location().line,
            parser.current_location().column,
            "Expected receiver name",
        ));
    };

    let receiver_type = parse_type(parser)?;
    if receiver_type.is_none() {
        return Err(CompilerError::syntax(
            parser.current_location().line,
            parser.current_location().column,
            "Expected receiver type",
        ));
    }

    parser.expect(&Token::RightParen)?;

    // Parse method name
    let method_name = if let Some(Token::Identifier(name)) = parser.peek().map(|t| &t.token) {
        let name = name.clone();
        parser.advance()?;
        name
    } else {
        return Err(CompilerError::syntax(
            parser.current_location().line,
            parser.current_location().column,
            "Expected method name",
        ));
    };

    // Add receiver as the first parameter
    let mut parameters = vec![crate::ast::types::Parameter {
        name: _receiver_name,
        param_type: receiver_type.unwrap(),
        default_value: None,
        location: parser.current_location(),
    }];

    // Parse method parameters
    parser.expect(&Token::LeftParen)?;

    if !parser.check(&Token::RightParen) {
        loop {
            if let Some(Token::Identifier(param_name)) = parser.peek().map(|t| &t.token) {
                let name = param_name.clone();
                parser.advance()?;
                parser.expect(&Token::Colon)?;

                let param_type = parse_type(parser)?;
                if param_type.is_none() {
                    return Err(CompilerError::syntax(
                        parser.current_location().line,
                        parser.current_location().column,
                        "Expected parameter type",
                    ));
                }

                parameters.push(crate::ast::types::Parameter {
                    name,
                    param_type: param_type.unwrap(),
                    default_value: None,
                    location: parser.current_location(),
                });
            }

            if !parser.consume(&Token::Comma)? {
                break;
            }
        }
    }

    parser.expect(&Token::RightParen)?;

    // Parse return type (Go-style: no arrow, just the type)
    let return_type = parse_type(parser)?;

    // Parse method body
    let body = if parser.consume(&Token::LeftBrace)? {
        let mut statements = Vec::new();
        while !parser.check(&Token::RightBrace) {
            if let Some(stmt) = parse_statement(parser)? {
                // Check if this is a terminating statement (return, break, continue)
                let is_terminating = matches!(
                    stmt,
                    crate::ast::stmt::Statement::Return(_)
                        | crate::ast::stmt::Statement::Break(_)
                        | crate::ast::stmt::Statement::Continue(_)
                );

                statements.push(stmt);

                // If we encountered a terminating statement, stop parsing
                // Skip any remaining statements until we reach the closing brace
                if is_terminating {
                    // Consume all tokens until we find the closing brace
                    while !parser.check(&Token::RightBrace) && !parser.is_at_end() {
                        parser.advance()?;
                    }
                    break;
                }
            } else {
                break;
            }
        }

        parser.expect(&Token::RightBrace)?;

        Some(Block {
            statements,
            location: parser.current_location(),
        })
    } else {
        None
    };

    Ok(Some(FunctionDecl {
        name: method_name,
        generics: vec![],
        parameters,
        return_type,
        body,
        attributes: vec![],
        location: parser.current_location(),
    }))
}
